import cn.gzzhome.utils.CommandBuilder
import cn.gzzhome.utils.CommandParamManager

/**
 * 此脚本封装了docker使用的常用工具
 */

def call(Closure cb) {
    cb.delegate = this
    return cb()
}

/**
 * 判断某些资源是否存在
 * @param type
 * @param name
 * @param args
 * @return
 */
def exists(type, name, args = "") {
    docker.withTool("docker") {
        if (type == "image") {
            return sh(script: "docker image ls ${name} --format {{.ID}} ${args}", returnStdout: true).trim()
        } else if (type == "volume") {
            return sh(script: "docker volume ls -f Name=${name} --format {{.Name}} ${args}", returnStdout: true).trim()
        } else {
            def ids = sh(script: "docker ${type} ls -f Name=${name} --format {{.ID}} ${args}", returnStdout: true).trim()
            if (ids) {
                def idArr = ids.split("\n")
                for (def id : idArr) {
                    def tmp = sh(script: "docker ${type} inspect --format {{.Name}} ${id}", returnStdout: true).replace("/", "").trim()
                    if (name == tmp) {
                        return id
                    }
                }
            }
            return null
        }
    }


}

/**
 * 判断容器是否已经存在
 * @param name
 * @param args
 * @return
 */
def existsContainer(name, args = "") {
    return exists("container", name, args)
}

/**
 * 判断镜像是否存在
 * @param name
 * @param args
 * @return
 */
def existsImage(name, args = "") {
    return exists("image", name, args)
}

/**
 * 判断是否存在卷
 * @param name
 * @param args
 * @return
 */
def existsVolume(name, args = "") {
    return exists("volume", name, args)
}

/**
 * 判断是否存在网络配置
 * @param name
 * @param args
 * @return
 */
def existsNetwork(name, args = "") {
    return exists("network", name, args)
}

/**
 * 重启容器
 * @param containerName
 * @return 返回状态码，成功为0
 */
def restart(String containerName) {
    docker.withTool("docker") {
        sh script: "docker container restart ${containerName}", returnStatus: true
    }
}

/**
 * 创建一个容器
 * @param imageName 镜像名
 * @param cb
 */
def run(imageName, Closure cb) {
    def builder = new CommandBuilder()
    CommandParamManager manager = new CommandParamManager([
            ["args", "此工具没包含的其它参数，可以在此值中添加", builder.allValue("", "")],
            ["environment", "环境变量", builder.allValue("-e")],
            ["hostname", "容器的hostname,docker会自创建一个hostname", builder.oneValue("--hostname", "")],
            ["link", "连接到其它的容器中，格式：<容器名 or 容器id>[:别名]", builder.allValue("--link")],
            ["name", "容器名", builder.oneValue("--name", "")],
            ["network", "连接到的网络，默认为default", builder.oneValue("--network", "")],
            ["port", "端口映射，格式：[<host port>:]<container port>", builder.allValue("--publish", "")],
            ["remove", "设置为ture表示删除，默认为false", builder.ifTrue("--rm")],
            ["volume", "添加卷映射，格式：<卷|宿主目录>:<容器中的目录>", builder.allValue("--volume", "\"")],
            ["restart", "失败后是否重新启动，可选值：\n" +
                    "no，默认策略，在容器退出时不重启容器\n" +
                    "on-failure，在容器非正常退出时（退出状态非0），才会重启容器\n" +
                    "on-failure:3，在容器非正常退出时重启容器，最多重启3次\n" +
                    "always，在容器退出时总是重启容器\n" +
                    "unless-stopped，在容器退出时总是重启容器，但是不考虑在Docker守护进程启动时就已经停止了的容器", builder.oneValue("--restart", "")],
            ["background", "是否后台执行，默认为true", { (!values || values[0]) ? "-d" : "-a STDOUT -a STDERR" }],
            ["image", "镜像名，格式：<镜像名>[:<版本，默认为latest>]", builder.oneValue("", ""), true],
            ["cmd", "传递给镜像的参数，此参数与docker的参数无关，可以添加多个", builder.allValue("", "")],
            ["pullAlways", "是否每次都拉取新的镜像,默认为true", { return "" }],
            ["deployAlways", "是否每次都重新部署，默认为true，设置为false则每次会提示是否重新部署。如果重启失败，脚本将尝试重新部署", { return "" }],
            ["quiet", "忽略所有提示，都使用默认值,默认为false", { return "" }]
    ])
    if (imageName) {
        manager.addDefault("image", [imageName])
    }
    def cmd = docker.withTool("docker") {
        manager.build("docker run", cb)
    }
    def containerNames = manager.getParamValues("name")
    //判断容器是否已经存在,"-a" 表示所有的镜像
    if (containerNames && existsContainer(containerNames[0], "-a")) {
        def deployAlways = manager.getParamValues("deployAlways")
        if (deployAlways && !deployAlways[0]) {
            def quiet = manager.getParamValues("quiet")
            def flag
            if (quiet && quiet[0]) {
                flag = false
            } else {
                flag = confirm {
                    params {
                        bool "flag", false, "重新部署"
                    }
                }
            }
            //如果指明deployAlways=false，则询问是否重新部署
            if (!flag) {
                def status = restart("${containerNames[0]}")
                if (status == 0) {
                    println("重启${containerNames[0]}成功")
                    return
                } else {
                    println("重启${containerNames[0]}失败，尝试重新部署")
                }
            }
        }
        //判断并删除容器
        docker.withTool("docker") {
            sh label: "关闭容器${containerNames[0]}", script: "docker container stop ${containerNames[0]}"
            sh label: "删除容器${containerNames[0]}", script: "docker container rm ${containerNames[0]}"
        }
    }
    def image = manager.getParamValues("image")[0]
    //判断镜像是否已经存在
    if (existsImage(image)) {
        def pullAlways = manager.getParamValues("pullAlways")
        if (!pullAlways || pullAlways[0]) {
            docker.withTool("docker") {
                sh label: "拉取镜像", script: "docker image pull ${image}"
            }
        }
    } else {
        docker.withTool("docker") {
            sh label: "未找到镜像${image}，拉取镜像", script: "docker image pull ${image}"
        }
    }
    //执行run命令并返回
    def background = manager.getParamValues("background")
    if (!background || background[0]) {
        return sh(script: cmd, returnStdout: true).trim()
    } else {
        sh script: cmd
    }

}

/**
 * 创建一个容器
 * @param cb
 * @return
 */
def run(Closure cb) {
    run("", cb)
}

/**
 * 构建镜像
 * @param imageName
 * @param cb
 * @return
 */
def build(imageName, Closure cb) {
    def builder = new CommandBuilder()
    CommandParamManager manager = new CommandParamManager([
            ["file", "Dockerfile文件名，默认为Dockerfile", builder.oneValue("--file", "")],
            ["alwaysRemoveContainer", "总是删除构建中的容器，默认为false", builder.ifTrue("--force-rm")],
            ["removeContainer", "构建成功后删除中间容器，默认为true", builder.ifTrue("--rm", true)],
            ["tag", "构建后添加的标签，可以有多个", builder.allValue("--tag", "")],
            ["network", "构建过程中的网络连接", builder.oneValue("--network", "")],
            ["noCache", "是否使用缓存构建", builder.ifTrue("--no-cache")],
            ["pullAlways", "是不是总是拉取最新的镜像，默认为true", builder.ifTrue("--pull", true)],
            ["quiet", "静默构建，只返回镜像id", builder.ifTrue("--quiet", false)],
            ["root", "构建根目录，默认为当前目录，即 .", builder.oneValue("", "")],
            ["removeOldTag", "如果镜像tag已经存在，是否尝试删除，默认为false", { return "" }],
            ["push", "是否自动推送,默认为false", { return "" }]
    ])

    manager.addDefault("root", ["."])
    if (imageName) {
        manager.paramMap["tag"].values += imageName
    }
    def cmd = docker.withTool("docker") {
        manager.build("docker build", cb)
    }
    def removeOldTag = manager.getParamValues("removeOldTag")
    if (removeOldTag && removeOldTag[0]) {
        //尝试清除原来的镜像
        def tags = manager.getParamValues("tag")
        tags.each {
            if (existsImage(it)) {
                docker.withTool("docker") {
                    sh "docker image rm ${it} | true"
                }
            }
        }
    }

    def quiet = manager.getParamValues("quiet")
    def result = ""
    if (quiet && quiet[0]) {
        result = sh(script: cmd, returnStdout: true).trim()
    } else {
        sh script: cmd
    }
    def push = manager.getParamValues("push")
    if (push && push[0]) {
        //推送镜像到创建
        def tags = manager.getParamValues("tag")
        docker.withTool("docker") {
            tags.each {
                sh "docker push ${it}"
            }
        }
    }

    return result

}

/**
 * 登录docker
 */
def login(registry = "") {
    def credit = confirm {
        params {
            passwordCredit("credit", true, "登录docker的凭证")
        }
    }
    withCredentials([usernamePassword(credentialsId: credit, passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')]) {
        docker.withTool("docker") {
            sh "docker login -u ${USERNAME} -p ${PASSWORD} ${registry}"
        }
    }
}

/**
 * 声明一个volume，并自动创建
 * @param name
 * @param cb
 * @return
 */
def volume(name, Closure cb) {
    if (!name) {
        throw new RuntimeException("未指定卷名")
    }
    if (!existsVolume(name)) {
        def builder = new CommandBuilder()
        CommandParamManager manager = new CommandParamManager([
                ["driver", "驱动", builder.oneValue("--driver", "")],
                ["options", "选项", builder.allValue("--opt")],
                ["name", "卷名", builder.oneValue("", "")]
        ])
        manager.addDefault("name", [name])
        manager.addDefault("driver", ["local"])
        def cmd = docker.withTool("docker") {
            manager.build("docker volume create", cb)
        }
        def result = sh(script: cmd, returnStdout: true).trim()
        return result
    }
    return name
}

/**
 * 创建一个nfs卷
 * @param name 卷名
 * @param addr 地址及路径，格式："<服务器地址>:<路径>"
 * @param access 访问方式，rw:读写（默认），ro只读
 */
def nfsVolume(name, addr, access = "rw") {
    if (addr.indexOf(":") < 0) {
        throw new RuntimeException("卷地址格式不正确，正确格式为：<服务器地址>:<路径>")
    }
    def ip = addr.substring(0, addr.indexOf(":"))
    def path = addr.substring(addr.indexOf(":") + 1)
    def driver = "nfs"
    def mType = Utils {
        machine()
    }
    if (mType.indexOf("arm") >= 0) {
        //arm构建的要改变一下driver
        driver = "nfs4"
    }
    volume(name, {
        options << [
                "type=${driver}",
                "o=addr=${ip},${access}",
                "device=:${path}"
        ]
    })
}

/**
 * docker exec
 * @param name
 * @param command
 */
def exec(name, command) {
    def cmd = "docker exec -t $name $command"
    def result = sh(script: cmd, returnStdout: true).trim()
    return result
}

/**
 * 管理网络配置
 * @param name
 * @param cb
 */
def network(name, Closure cb) {
    def builder = new CommandBuilder()
    CommandParamManager manager = new CommandParamManager([
            ["attachable", "是否可以自定义添加", builder.ifTrue("--attachable")],
            ["driver", "网络驱动器", builder.oneValue("-d", "")],
            ["option", "可选项", builder.allValue("-o")],
            ["label", "标签", builder.allValue("--label", "")],
            ["name", "网络名", builder.oneValue("", "")]
    ])
    manager.addDefault("name", [name])
    manager.addDefault("driver", ["bridge"])
    def cmd = docker.withTool("docker") {
        manager.build("docker network create ", cb)
    }
    //查询是否已经存在网络
    if (!existsNetwork(name)) {
        def result = sh(script: cmd, returnStdout: true).trim()
        return result
    }
}
